// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package noder

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/types2"
	"cmd/internal/src"
	"strings"
)

func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
	switch pkg {
	case nil:
		return types.BuiltinPkg
	case g.self:
		return types.LocalPkg
	case types2.Unsafe:
		return types.UnsafePkg
	}
	return types.NewPkg(pkg.Path(), pkg.Name())
}

var universeAny = types2.Universe.Lookup("any").Type()

// typ转换类型2。键入一个类型。类型，包括之前
// 已翻译类型的缓存。
func (g *irgen) typ(typ types2.Type) *types.Type {
	// 在我们完全定义了
	// （可能是递归的）顶级类型之前，推迟CheckSize调用。
	types.DeferCheckSize()
	res := g.typ1(typ)
	types.ResumeCheckSize()

	// 完成typesToFinalize上的所有类型，现在我们处于
	// 完全定义（可能是递归）类型的顶部。fillinMethods可以创建更多
	// 类型来完成。
	for len(g.typesToFinalize) > 0 {
		l := len(g.typesToFinalize)
		info := g.typesToFinalize[l-1]
		g.typesToFinalize = g.typesToFinalize[:l-1]
		types.DeferCheckSize()
		g.fillinMethods(info.typ, info.ntyp)
		types.ResumeCheckSize()
	}
	return res
}

// typ1与typ类似，但不调用CheckSize，因为它可能只有
// 递归类型的构造部分。不应从
// 文件外部调用（g.typ是“外部”入口点）。
func (g *irgen) typ1(typ types2.Type) *types.Type {
	// 请参阅问题49583：类型检查器无法跟踪别名，
	// 但是对于任何常见的别名，我们都可以通过保留一个
	// 指针标识来改进，在格式化类型字符串时可以对其进行检查。
	if typ == universeAny {
		return types.AnyType
	}
	// 缓存类型2到类型映射。重要信息，以便每个定义的泛型
	// 类型（实例化或未实例化）都有一个单独的类型。类型表示。
	// 还可以避免重复转换
	// 类型2，从而节省大量计算和内存。
	res, ok := g.typs[typ]
	if !ok {
		res = g.typ0(typ)
		// 计算前端看到的所有混凝土类型的尺寸。
		// 这是对types1 
		// typechecker中CheckSize（）调用的替换。这些将被推迟到顶级g.typ（）。
		if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() {
			types.CheckSize(res)
		}
		g.typs[typ] = res
	}
	return res
}

// instTypeName2基于类型参数
// （以types2类型给出）为实例化类型创建名称。
func (g *irgen) instTypeName2(name string, targs *types2.TypeList) string {
	rparams := make([]*types.Type, targs.Len())
	for i := range rparams {
		rparams[i] = g.typ(targs.At(i))
	}
	return typecheck.InstTypeName(name, rparams)
}

// typ0转换类型2。键入一个类型。键入，但不在顶层执行缓存检查
// 。
func (g *irgen) typ0(typ types2.Type) *types.Type {
	switch typ := typ.(type) {
	case *types2.Basic:
		return g.basic(typ)
	case *types2.Named:
		// 如果设置了tparams，但没有设置targs，则typ是基本的通用
		// 类型。typ作为别名
		// 的源类型的一部分出现，因为这是唯一一个不涉及实例化的泛型类型。我们只是使用g.obj（）将
		// 中的命名类型转换为下面的普通方式。
		if typ.TypeParams() != nil && typ.TypeArgs() != nil {
			// typ是已定义（命名）泛型类型的实例化。
			// 此实例化也应该是已定义（命名）的类型。
			// types2为我们提供了t中的替换类型。subground（）
			// 被替换的类型可能有，也可能没有类型
			// params。例如，我们可能用一个类型
			// param替换另一个类型param。
			// 
			// 转换为类型时。Type，typ有一个唯一的名称
			// 基于类型参数的名称。
			instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
			s := g.pkg(typ.Obj().Pkg()).Lookup(instName)

			// 确保type1中存在基本泛型类型（如果我们引用的是导入的泛型类型，则可能是
			// 还没有，因为
			// 与此包中声明的泛型类型相反）。如果在导入base时定义了
			// s.Def（如果导入了
			// type），请确保在检查s.Def之前执行此查找。（第50486期）。
			base := g.obj(typ.Origin().Obj())

			if s.Def != nil {
				// 我们已经遇到了这个实例化。
				// 使用我们之前创建的类型，因为那里的
				// 必须正好是定义类型的一个实例。
				return s.Def.Type()
			}

			if base.Class == ir.PAUTO {
				// 如果基类型是本地类型，我们想在离开包含块时弹出
				// 这个实例化的类型符号/定义，这样我们以后就不会错误地使用它了。
				types.Pushdcl(s)
			}

			// 首先创建一个转发类型，并将其放入g.typs 
			// 映射中，以便处理递归泛型
			// （包括通过方法签名）。在转换基础类型
			// NTIP信息（Def，RParams，这可能会设置
			// HasTParam），以便正确处理递归。
			// 本身之前，设置额外的
			ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
			g.typs[typ] = ntyp

			// 如果NTIP仍然有类型参数，那么我们必须是
			// 引用类似“value[T2]”的内容，就像
			// 指定方法的通用接收器时一样，其中
			// value被定义为“type value[T any]”。保存
			// 类型args，它现在将成为
			// 当前类型的新类型参数。
			// 
			// 如果NTIP没有类型参数，我们将保存用于实例化此类型的
			// 非泛型类型。我们将在实例化
			// 实例化类型。
			targs := typ.TypeArgs()
			rparams := make([]*types.Type, targs.Len())
			for i := range rparams {
				rparams[i] = g.typ1(targs.At(i))
			}
			ntyp.SetRParams(rparams)
			// fmt。Printf（“Saw new type%v%v\n”，instName，ntyp.HasTParam（））

			// 保存基本泛型类型的符号。
			ntyp.SetOrigType(base.Type())
			ntyp.SetUnderlying(g.typ1(typ.Underlying()))
			if typ.NumMethods() != 0 {
				// 保存对g.fillinMethods（）的延迟调用（一旦
				// 潜在的递归类型已完全解决）。
				g.typesToFinalize = append(g.typesToFinalize,
					&typeDelayInfo{
						typ:  typ,
						ntyp: ntyp,
					})
			}
			return ntyp
		}
		obj := g.obj(typ.Obj())
		if obj.Op() != ir.OTYPE {
			base.FatalfAt(obj.Pos(), "expected type: %L", obj)
		}
		return obj.Type()

	case *types2.Array:
		return types.NewArray(g.typ1(typ.Elem()), typ.Len())
	case *types2.Chan:
		return types.NewChan(g.typ1(typ.Elem()), dirs[typ.Dir()])
	case *types2.Map:
		return types.NewMap(g.typ1(typ.Key()), g.typ1(typ.Elem()))
	case *types2.Pointer:
		return types.NewPtr(g.typ1(typ.Elem()))
	case *types2.Signature:
		return g.signature(nil, typ)
	case *types2.Slice:
		return types.NewSlice(g.typ1(typ.Elem()))

	case *types2.Struct:
		fields := make([]*types.Field, typ.NumFields())
		for i := range fields {
			v := typ.Field(i)
			f := types.NewField(g.pos(v), g.selector(v), g.typ1(v.Type()))
			f.Note = typ.Tag(i)
			if v.Embedded() {
				f.Embedded = 1
			}
			fields[i] = f
		}
		return types.NewStruct(g.tpkg(typ), fields)

	case *types2.Interface:
		embeddeds := make([]*types.Field, typ.NumEmbeddeds())
		j := 0
		for i := range embeddeds {
			// TODO（mdempsky）：获取嵌入位置。
			e := typ.EmbeddedType(i)

			// 使用Go 1.18，嵌入式元素可以是任何类型，而不是
			// 只是一个接口。
			embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
			j++
		}
		embeddeds = embeddeds[:j]

		methods := make([]*types.Field, typ.NumExplicitMethods())
		for i := range methods {
			m := typ.ExplicitMethod(i)
			mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature))
			methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
		}

		return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...), typ.IsImplicit())

	case *types2.TypeParam:
		// 在类型的sym中保存类型参数的名称。
		// 在符号名
		pkg := g.tpkg(typ)
		// 中包含类型2下标，使用类型参数的上下文
		// 和函数、类型或方法声明，为类型参数创建唯一的类型1名称。此外，根据类型参数索引，将空白类型
		// 参数名称映射为唯一名称。
		// 唯一的空白名称将被导出，但将在
		// types2和gcimporter导入期间恢复。
		assert(g.curDecl != "")
		nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index())
		sym := pkg.Lookup(nm)
		if sym.Def != nil {
			// 确保我们对相同的
			// 名称使用相同的类型参数type，无论它是在types1导入期间创建的还是在
			// 此types2到types1翻译期间创建的。
			return sym.Def.Type()
		}
		tp := types.NewTypeParam(sym, typ.Index())
		nname := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym)
		sym.Def = nname
		nname.SetType(tp)
		tp.SetNod(nname)
		// 如果绑定方法引用typ，则设置g.typs[typ]。
		g.typs[typ] = tp

		bound := g.typ1(typ.Constraint())
		tp.SetBound(bound)
		return tp

	case *types2.Union:
		nt := typ.Len()
		tlist := make([]*types.Type, nt)
		tildes := make([]bool, nt)
		for i := range tlist {
			t := typ.Term(i)
			tlist[i] = g.typ1(t.Type())
			tildes[i] = t.Tilde()
		}
		return types.NewUnion(tlist, tildes)

	case *types2.Tuple:
		// 元组用于函数调用的类型（即函数的返回值）。
		if typ == nil {
			return (*types.Type)(nil)
		}
		fields := make([]*types.Field, typ.Len())
		for i := range fields {
			fields[i] = g.param(typ.At(i))
		}
		t := types.NewStruct(types.LocalPkg, fields)
		t.StructType().Funarg = types.FunargResults
		return t

	default:
		base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ)
		panic("unreachable")
	}
}

// fillinMethods使用至少一个方法为定义的类型填充方法名节点和类型。这是以后在查找
// 实例化类型的方法时进行类型检查，以及实际生成实例化
// 类型的方法时需要的。
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
	targs2 := typ.TypeArgs()
	targs := make([]*types.Type, targs2.Len())
	for i := range targs {
		targs[i] = g.typ1(targs2.At(i))
	}

	methods := make([]*types.Field, typ.NumMethods())
	for i := range methods {
		m := typ.Method(i)
		recvType := deref2(types2.AsSignature(m.Type()).Recv().Type())
		var meth *ir.Name
		imported := false
		if m.Pkg() != g.self {
			// 导入的方法不能按名称加载（就像
			// g.obj（）那样）——它们必须通过其
			// 类型加载。
			meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name)
			imported = true
		} else {
			meth = g.obj(m)
		}
		assert(recvType == types2.Type(typ))
		if imported {
			// 不幸的是，meth是
			// 泛型类型的方法的类型，因此我们必须进行替换以获得
			// 实例化类型的方法的名称/类型，
			// 使用m.type（）。RParams（）和typ。targets（）
			inst2 := g.instTypeName2("", typ.TypeArgs())
			name := meth.Sym().Name
			i1 := strings.Index(name, "[")
			i2 := strings.Index(name[i1:], "]")
			assert(i1 >= 0 && i2 >= 0)
			// 生成实例化方法的名称。
			name = name[0:i1] + inst2 + name[i1+i2+1:]
			newsym := meth.Sym().Pkg.Lookup(name)
			var meth2 *ir.Name
			if newsym.Def != nil {
				meth2 = newsym.Def.(*ir.Name)
			} else {
				meth2 = ir.NewNameAt(meth.Pos(), newsym)
				rparams := types2.AsSignature(m.Type()).RecvTypeParams()
				tparams := make([]*types.Type, rparams.Len())
				// 将g.curDecl设置为方法上下文，因此在我们要翻译的方法的接收者中键入
				// params获得正确的唯一名称。我们可以将
				// 放在顶级typeDecl中，因此保存并恢复
				// g.curDecl的当前内容。
				savedCurDecl := g.curDecl
				g.curDecl = typ.Obj().Name() + "." + m.Name()
				for i := range tparams {
					tparams[i] = g.typ1(rparams.At(i))
				}
				g.curDecl = savedCurDecl
				assert(len(tparams) == len(targs))
				ts := typecheck.Tsubster{
					Tparams: tparams,
					Targs:   targs,
				}
				// 替换类型
				meth2.SetType(ts.Typ(meth.Type()))
				newsym.Def = meth2
			}
			meth = meth2
		}
		methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
		methods[i].Nname = meth
	}
	ntyp.Methods().Set(methods)
	if !ntyp.HasTParam() && !ntyp.HasShape() {
		// 为新的完全实例化类型生成所有方法。
		typecheck.NeedInstType(ntyp)
	}
}

func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
	tparams2 := sig.TypeParams()
	tparams := make([]*types.Field, tparams2.Len())
	for i := range tparams {
		tp := tparams2.At(i).Obj()
		tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type()))
	}

	do := func(typ *types2.Tuple) []*types.Field {
		fields := make([]*types.Field, typ.Len())
		for i := range fields {
			fields[i] = g.param(typ.At(i))
		}
		return fields
	}
	params := do(sig.Params())
	results := do(sig.Results())
	if sig.Variadic() {
		params[len(params)-1].SetIsDDD(true)
	}

	return types.NewSignature(g.tpkg(sig), recv, tparams, params, results)
}

func (g *irgen) param(v *types2.Var) *types.Field {
	return types.NewField(g.pos(v), g.sym(v), g.typ1(v.Type()))
}

func (g *irgen) sym(obj types2.Object) *types.Sym {
	if name := obj.Name(); name != "" {
		return g.pkg(obj.Pkg()).Lookup(obj.Name())
	}
	return nil
}

func (g *irgen) selector(obj types2.Object) *types.Sym {
	pkg, name := g.pkg(obj.Pkg()), obj.Name()
	if types.IsExported(name) {
		pkg = types.LocalPkg
	}
	return pkg.Lookup(name)
}

// tpkg返回函数、接口、结构或类型参数类型
// 表达式出现的包。
// 
// 警告：对于退化类型“func（）”、“interface{}”和
// “struct{}”，tpkg始终返回LocalPkg。但是，我们只需要
// 程序包信息，这样go/types就可以通过其API报告它，而
// 我们无法返回这些
// 特定类型的原始程序包，是因为go/types*不*为
// 它们报告它。所以在实践中，这个限制可能是没有意义的。
func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
	if obj := anyObj(typ); obj != nil {
		return g.pkg(obj.Pkg())
	}
	return types.LocalPkg
}

// anyObj返回一些可以从typ访问的对象（如果有的话）。
func anyObj(typ types2.Type) types2.Object {
	switch typ := typ.(type) {
	case *types2.Signature:
		if recv := typ.Recv(); recv != nil {
			return recv
		}
		if params := typ.Params(); params.Len() > 0 {
			return params.At(0)
		}
		if results := typ.Results(); results.Len() > 0 {
			return results.At(0)
		}
	case *types2.Struct:
		if typ.NumFields() > 0 {
			return typ.Field(0)
		}
	case *types2.Interface:
		if typ.NumExplicitMethods() > 0 {
			return typ.ExplicitMethod(0)
		}
	case *types2.TypeParam:
		return typ.Obj()
	}
	return nil
}

func (g *irgen) basic(typ *types2.Basic) *types.Type {
	switch typ.Name() {
	case "byte":
		return types.ByteType
	case "rune":
		return types.RuneType
	}
	return *basics[typ.Kind()]
}

var basics = [...]**types.Type{
	types2.Invalid:        new(*types.Type),
	types2.Bool:           &types.Types[types.TBOOL],
	types2.Int:            &types.Types[types.TINT],
	types2.Int8:           &types.Types[types.TINT8],
	types2.Int16:          &types.Types[types.TINT16],
	types2.Int32:          &types.Types[types.TINT32],
	types2.Int64:          &types.Types[types.TINT64],
	types2.Uint:           &types.Types[types.TUINT],
	types2.Uint8:          &types.Types[types.TUINT8],
	types2.Uint16:         &types.Types[types.TUINT16],
	types2.Uint32:         &types.Types[types.TUINT32],
	types2.Uint64:         &types.Types[types.TUINT64],
	types2.Uintptr:        &types.Types[types.TUINTPTR],
	types2.Float32:        &types.Types[types.TFLOAT32],
	types2.Float64:        &types.Types[types.TFLOAT64],
	types2.Complex64:      &types.Types[types.TCOMPLEX64],
	types2.Complex128:     &types.Types[types.TCOMPLEX128],
	types2.String:         &types.Types[types.TSTRING],
	types2.UnsafePointer:  &types.Types[types.TUNSAFEPTR],
	types2.UntypedBool:    &types.UntypedBool,
	types2.UntypedInt:     &types.UntypedInt,
	types2.UntypedRune:    &types.UntypedRune,
	types2.UntypedFloat:   &types.UntypedFloat,
	types2.UntypedComplex: &types.UntypedComplex,
	types2.UntypedString:  &types.UntypedString,
	types2.UntypedNil:     &types.Types[types.TNIL],
}

var dirs = [...]types.ChanDir{
	types2.SendRecv: types.Cboth,
	types2.SendOnly: types.Csend,
	types2.RecvOnly: types.Crecv,
}

// 如果是指针类型，deref2执行类型2类型t的单个deref。
func deref2(t types2.Type) types2.Type {
	if ptr := types2.AsPointer(t); ptr != nil {
		t = ptr.Elem()
	}
	return t
}
